package com.gitee.wsl.mathematics.vector

import com.gitee.wsl.io.dataproxy.DataGetContainer
import com.gitee.wsl.io.dataproxy.DataProxyContainer
import com.gitee.wsl.mathematics.number.NumberCalcContainer
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

interface VectorCalcContainer<T:Number,V:DataGetContainer<T>>: NumberCalcContainer<T>, DataGetContainer<T> {

    val valueArray: List<T> get() = proxy.values()

    val self:V

    val isZero get() = proxy.all { it.isApproxZero() }

    val isNotZero get() = !isZero

    fun create(values: List<T>):V

    val abs: V
        get() = create(proxy.map{ i ->  abs(i)  })
    val sign:V
        get() = create(proxy.map{ i ->  sign(i)  })
    val ceil:V
        get() = create(proxy.map{ i ->  ceil(i)  })
    val floor:V
        get() = create(proxy.map{ i ->  floor(i)  })
    val round:V
        get() = create(proxy.map{ i ->  round(i)  })

    operator fun plus(other: V) :V = create(proxy.mapIndexed{ index, i ->  i + other.proxy[index] })
    operator fun plus(other: Number) :V = create(proxy.map{  i ->  i + other})
    fun plusBy(other: Number) :V = create(proxy.map{  i ->  i + other})

    operator fun minus(other: V) :V = create(proxy.mapIndexed{ index, i ->  i - other.proxy[index] })
    operator fun minus(other: Number) :V = create(proxy.map{  i ->  i - other})
    fun minusBy(other: Number) :V = create(proxy.map{ i -> other - i })

    operator fun times(a: Number) :V = create( proxy.map{ i ->  i * a })

    operator fun div(a: Number):V = create( proxy.map{ i ->  i / a })
    fun divBy(a: Number) :V = create( proxy.map{ i ->  i.divBy(a) })

    operator fun rem(a: Number):V = create( proxy.map{ i ->  i.rem(a) })
    fun remBy(a: Number) :V =create( proxy.map{  i ->  i.remBy(a) })

    operator fun unaryMinus() : V = times(-1)

}

interface VectorModifyContainer<T:Number,V:DataGetContainer<T>>: NumberCalcContainer<T>, DataProxyContainer<T>{

    //val mutex:Mutex

    //fun set(other:V) = proxy.updateIndexed { index, _ -> other.proxy[index] }

    fun clear() = proxy.fill(zero)

    fun abs()   = proxy.update {  t -> abs(t) }
    fun sign()  = proxy.update {  t -> sign(t) }
    fun ceil()  = proxy.update {  t -> ceil(t) }
    fun floor() = proxy.update {  t -> floor(t) }
    fun round() = proxy.update {  t -> round(t) }


    operator fun timesAssign(v: Number) = proxy.update {  t -> t * v }

    operator fun divAssign(v: Number) = proxy.update {  t -> t / v }

    //fun subtract(other: VectorModifyContainer<T,*>) = proxy.updateIndexed { index, t -> t - other.proxy[index] }

    //operator fun plusAssign(other: VectorModifyContainer<T,*>) = proxy.updateIndexed { index, t -> t + other.proxy[index] }

}

inline fun <T:Number,V: DataGetContainer<T>, M: VectorModifyContainer<T, V>> M.update(block: M.()->Unit):M{
     block()
    return this
}

suspend inline fun <T:Number,V: DataGetContainer<T>, M: VectorModifyContainer<T, V>> M.suspendUpdate(mutex:Mutex,block: M.()->Unit):M {
    mutex.withLock { block() }
    return this
}